﻿module Leaves.NeuroCryptography.Cryptography

open System
open Leaves.NeuroCryptography.NeuralNetworks
open Leaves.NeuroCryptography.ChaoticFunctions

let CreateHashNetwork inputLength outputLength =
    let inputsPerNeuron = 4

    if inputLength <= 0 then
        failwith "input length should be greater that 0"
    if inputLength % inputsPerNeuron <> 0 then
        failwithf "input length should be a product of %d" inputsPerNeuron
    if outputLength <= 0 then
        failwith "output length should be greater that 0"

    let middleLength = inputLength / inputsPerNeuron

    let createEmptyInputWeights inputLength =
        seq {
            for _ in 1..inputLength do
                yield 0.0
        } |> Seq.toList

    let inputLayer =
        seq {
            for neuronIndex in 1..middleLength do
                let maxInputIndex = inputsPerNeuron * neuronIndex - 1
                let minInputIndex = inputsPerNeuron * (neuronIndex - 1)

                let neuronInputWeights = createEmptyInputWeights 4
                let neuronInputs =
                    seq {
                        for inputIndex in minInputIndex..maxInputIndex do
                            let inputWeight = neuronInputWeights.[inputIndex - minInputIndex]
                            yield {Weight = inputWeight; InputIndex = inputIndex}
                    } |> Seq.toList
                
                yield {Inputs = neuronInputs; Function = EmptyChaoticFunction; Bias = 0.0}

        } |> Seq.toList

    let createCommonLayer inputLength outputLength =
        seq {
            for neuronIndex in 1..outputLength do
                let inputWeights = createEmptyInputWeights inputLength

                let createInputFromWeight index weight =
                    {Weight = weight; InputIndex = index}

                let neuronInputs = List.mapi createInputFromWeight inputWeights

                yield {Inputs = neuronInputs; Function = EmptyChaoticFunction; Bias = 0.0}
        } |> Seq.toList

    let diffusionLayer = createCommonLayer middleLength middleLength
    let outputLayer = createCommonLayer middleLength outputLength

    {Layers = [inputLayer; diffusionLayer; outputLayer]}
